#pragma once
#include"cell.cpp"

void read_atpg_lib(std::string& lib_path)
{
    std::fstream atpg_lib(lib_path.c_str(),std::ios::in);

    std::string cmd;

    std::unordered_set<std::size_t> unique_hash{std::size_t(0)};
    
    while(getline(atpg_lib,cmd))
    {
        if(cmd.find("\"name\": [")!=std::string::npos)
        {
            cell cur;
            cur.wire.emplace_back(generate_string_hash(unique_hash,port_hash,"1'b0"),'0','0');
            cur.wire.emplace_back(generate_string_hash(unique_hash,port_hash,"1'b1"),'1','1');

            std::vector<std::string> cell_name;

            while (getline(atpg_lib,cmd))
            {
                if(cmd.find("],")!=std::string::npos) break;
                trim(cmd);
                cell_name.emplace_back(cmd);
            }

            while(getline(atpg_lib,cmd))
            {
                if(cmd.find("\"input\": [")!=std::string::npos)
                {
                    if(cmd.find("]")!=std::string::npos) continue;
                    
                    while(getline(atpg_lib,cmd))
                    {
                        if(cmd.find("]")!=std::string::npos) break;
                        trim(cmd);
                        cur.wire.emplace_back(generate_string_hash(unique_hash,port_hash,cmd),'x','x');
                        cur.input_port.emplace_back(generate_string_hash(unique_hash,port_hash,cmd),0,cur.wire.size()-1,nullptr);
                    }
                }
                else if(cmd.find("\"output\": [")!=std::string::npos)
                {
                    
                    while(getline(atpg_lib,cmd))
                    {
                        if(cmd.find("]")!=std::string::npos) break;
                        trim(cmd);
                        cur.wire.emplace_back(generate_string_hash(unique_hash,port_hash,cmd),'x','x');
                        cur.output_port.emplace_back(generate_string_hash(unique_hash,port_hash,cmd),0,cur.wire.size()-1,std::vector<cell*>{});
                    }
                }
                else if(cmd.find("\"wire\": [")!=std::string::npos)
                {
                    if(cmd.find("]")!=std::string::npos) break;
                    while(getline(atpg_lib,cmd))
                    {
                        if(cmd.find("]")!=std::string::npos) break;
                        trim(cmd);
                        cur.wire.emplace_back(generate_string_hash(unique_hash,port_hash,cmd),'x','x');
                    }
                }
                else if(cmd.find("],")!=std::string::npos)
                    break;
            }

            while(getline(atpg_lib,cmd))
            {
                if(cmd.find("\"sim_type\": ")!=std::string::npos)
                {
                    if(cmd.find("DFF_PRI")!=std::string::npos)
                    {
                        cur.sim.emplace_back(pri::DFF_PRI,std::vector<char*>{});
                        cur.is_dff = 1;
                    }
                    else if(cmd.find("XNOR")!=std::string::npos)
                    {
                        cur.sim.emplace_back(pri::XNOR,std::vector<char*>{});
                    }
                    else if(cmd.find("XOR")!=std::string::npos)
                    {
                        cur.sim.emplace_back(pri::XOR,std::vector<char*>{});
                    }
                    else if(cmd.find("AND")!=std::string::npos&&cmd.find("NAND")==std::string::npos)
                    {
                        cur.sim.emplace_back(pri::AND,std::vector<char*>{});
                    }
                    else if(cmd.find("NAND")!=std::string::npos)
                    {
                        cur.sim.emplace_back(pri::NAND,std::vector<char*>{});
                    }
                    else if(cmd.find("OR")!=std::string::npos&&cmd.find("NOR")==std::string::npos)
                    {
                        cur.sim.emplace_back(pri::OR,std::vector<char*>{});
                    }
                    else if(cmd.find("NOR")!=std::string::npos)
                    {
                        cur.sim.emplace_back(pri::NOR,std::vector<char*>{});
                    }
                    else if(cmd.find("BUF")!=std::string::npos)
                    {
                        cur.sim.emplace_back(pri::BUF,std::vector<char*>{});
                    }
                    else if(cmd.find("NOT")!=std::string::npos)
                    {
                        cur.sim.emplace_back(pri::NOT,std::vector<char*>{});
                    }
                    else if(cmd.find("MUX_2")!=std::string::npos)
                    {
                        cur.sim.emplace_back(pri::MUX_2,std::vector<char*>{});
                    }
                }
                else if(cmd.find("\"connection\": [")!=std::string::npos)
                {
                    std::vector<std::pair<char,char>> tmp_para;
                    while(getline(atpg_lib,cmd))
                    {
                        if(cmd.find("]")!=std::string::npos) break;
                        trim(cmd);
                        std::size_t wire_hash = generate_string_hash(unique_hash,port_hash,cmd);
                        for(int i = 0;i<cur.wire.size();++i)
                            if(std::get<0>(cur.wire[i]) == wire_hash)
                            {
                                tmp_para.emplace_back(i,1);
                                break;
                            }
                    }
                    cur.uninitial_para.emplace_back(tmp_para);
                }
                else if(cmd.find("]")!=std::string::npos&&cmd.find(",")==std::string::npos) break;
            }

            if(cur.is_dff)
            {
                int index_dff_pri = 0 ;
                for(;index_dff_pri<cur.sim.size();++index_dff_pri)
                    if(cur.sim[index_dff_pri].first==pri::DFF_PRI)
                        break;

                std::vector<std::size_t> tmp_port;
                cur.async_port.resize(3,0);
                
                if(std::get<0>(cur.wire[cur.uninitial_para[index_dff_pri][1].first]) != generate_string_hash(unique_hash,port_hash,"1'b0"))
                    tmp_port.emplace_back(std::get<0>(cur.wire[cur.uninitial_para[index_dff_pri][1].first]));

                if(std::get<0>(cur.wire[cur.uninitial_para[index_dff_pri][2].first]) !=generate_string_hash(unique_hash,port_hash,"1'b0"))
                    tmp_port.emplace_back(std::get<0>(cur.wire[cur.uninitial_para[index_dff_pri][2].first]));

                tmp_port.emplace_back(std::get<0>(cur.wire[cur.uninitial_para[index_dff_pri][3].first]));

                for(int k=0;k<tmp_port.size();++k)
                {
                    for(int i=0;i<index_dff_pri;++i)
                        if(std::get<0>(cur.wire[cur.uninitial_para[i][0].first]) == tmp_port[k] )
                        {
                            tmp_port[k] = std::get<0>(cur.wire[cur.uninitial_para[i][1].first]);

                            for(int j=1;j<cur.uninitial_para[i].size();++j)
                                tmp_port.emplace_back(std::get<0>(cur.wire[cur.uninitial_para[i][j].first]));
            
                            i=0;
                        }
                }
                std::sort(tmp_port.begin(),tmp_port.end());

                for(auto end = std::unique(tmp_port.begin(),tmp_port.end()),it=tmp_port.begin();it!=end;++it)
                    cur.async_port[it-tmp_port.begin()] = *it;

                tmp_port.clear();

                tmp_port.emplace_back((std::get<0>(cur.wire[cur.uninitial_para[index_dff_pri][4].first])));   
                cur.uninitial_para[index_dff_pri][4].second = 0;     

                for(int k=0;k<tmp_port.size();++k)
                {
                    for(int i=0;i<index_dff_pri;++i)
                        if(std::get<0>(cur.wire[cur.uninitial_para[i][0].first]) == tmp_port[k] )
                        {
                            tmp_port[k] = std::get<0>(cur.wire[cur.uninitial_para[i][1].first]);
                            cur.uninitial_para[i][1].second = 0;
                            for(int j=1;j<cur.uninitial_para[i].size();++j)
                            {
                                tmp_port.emplace_back(std::get<0>(cur.wire[cur.uninitial_para[i][j].first]));
                                cur.uninitial_para[i][j].second = 0;
                            }
                            i=0;
                        }
                }
            }


            for(auto& s : cell_name)
                tech_lib[s]=cur;
        }
    }

    atpg_lib.close();

    for(auto& [a,b] : port_hash )
        hash_port[b] = a;
}
